#ifndef _TEST_BACKTRACKING_H_
#define _TEST_BACKTRACKING_H_
#include <cppunit/extensions/HelperMacros.h>

class Test_homework: public CppUnit::TestFixture
{
        CPPUNIT_TEST_SUITE( Test_homework);
        //CPPUNIT_TEST (testcase_01);
	CPPUNIT_TEST (testcase_geodesics_01);
        CPPUNIT_TEST_SUITE_END();

public :
        void setUp();
        void tearDown();
		void testcase_01();
		void testcase_geodesics_01();
private:
        typedef Backtracking <double>::MATRIX           MATRIX;
        typedef Backtracking <double>::SCALAR           SCALAR;
        typedef Backtracking <double>::VECTOR           VECTOR;
        typedef Backtracking <double>::POINT            POINT; 
        typedef Backtracking <double>::ARRAY            ARRAY;
        typedef Backtracking <double>::SEGMENT          SEGMENT; 
        //void test_draw_geodesics_generic(const char * argv, int start_vertex_index, unsigned center_face_idx);

        //there are some functions which help to check the errors of models

private:
	bool read_check_manifold_model(Mesh & mesh, std::string filename)
	{
		OpenMesh::IO::Options ropt, wopt;

		if (! OpenMesh::IO::read_mesh(mesh, filename.c_str(), ropt))
		{
			std::cerr<<"Error loading mesh from file "<<filename.c_str()<<std::endl;
			return false;
		}
		//cout<<mesh.n_vertices()<<endl;
		//unsigned n_vertices = mesh.n_vertices();
		//Mesh::VertexIter v_it, v_end (mesh.vertices_end());
	
		//Mesh::VertexHandle v_handle = mesh.vertex_handle(0);
		//cout<<mesh.point (v_handle)<<endl;

		Mesh::VertexIter v_it, v_end (mesh.vertices_end());
		for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
		{
			if (! mesh.is_manifold(v_it) )
			{
				std::cerr<<"Not manifold"<<std::endl;
				return false;
			}
		}
		return true;
	}

	void vertex_point_fmm (Mesh & mesh, int vertex_idx)
	{
		std::vector<Mesh::VertexHandle> boundary;
		boundary.push_back(mesh.vertex_handle(vertex_idx));
		mesh.data(mesh.vertex_handle(vertex_idx)).set_distance(0);
		fast_marching (mesh, boundary);
	}
	void vertex_point_set_fmm (Mesh& mesh, std::vector<int> point_idx_set)
	{
		std::vector<Mesh::VertexHandle> boundary;
		for (unsigned i = 0;i < point_idx_set.size(); ++i)
		{
			boundary.push_back(mesh.vertex_handle(point_idx_set[i]));
			mesh.data(mesh.vertex_handle(point_idx_set[i])).set_distance(0);
			mesh.data(mesh.vertex_handle(point_idx_set[i])).set_state(ALIVE);
		}
		fast_marching (mesh, boundary);
	}
	void face_centroid_fmm (Mesh & mesh, int face_idx)
	{
		POINT centroid (0,0,0); 
		Mesh::FaceHandle start_face = mesh.face_handle(face_idx);
		for (Mesh::FaceVertexIter fv_it  = mesh.fv_iter(start_face); fv_it; ++fv_it)
		{
		
			centroid [0] +=mesh.point(fv_it)[0];
			centroid [1] +=mesh.point(fv_it)[1];
			centroid [2] +=mesh.point(fv_it)[2];
		}
		centroid [0] /= 3.0f;
		centroid [1] /= 3.0f;
		centroid [2] /= 3.0f;

		face_point_fmm (mesh, face_idx, centroid);
	}

	void generate_geodesics (const char * input_mesh, const char * out_mesh, unsigned backtracking_face_idx, unsigned start_vertex_idx)
	{
		Mesh mesh;

		//bool result =  this->read_check_manifold_model(mesh, "../../data/Armadillo_20qem.obj");
		bool result =  this->read_check_manifold_model(mesh, input_mesh);
		//bool result =  this->read_check_manifold_model(mesh, "../../data/plane5.obj");
		if (!result) {CPPUNIT_ASSERT_ASSERTION_FAIL("The model is invalid!"); return;}

		std::cerr<<"Fast Marching..."<<std::endl;
		//FMM starting point 
		int point_idx = start_vertex_idx; 
		this->vertex_point_fmm (mesh, point_idx);
		std::vector<Mesh::FaceHandle>  vec_end_face_handle ;
		for (Mesh::VertexFaceIter vf_it = mesh.vf_iter (mesh.vertex_handle(point_idx)); vf_it ; ++vf_it)
		{
			vec_end_face_handle.push_back(vf_it.handle());
		}
		Mesh::Point end = mesh.point(mesh.vertex_handle(point_idx));


		std::cerr<<"Draw Geodesics..."<<std::endl;
		unsigned center_face_idx = backtracking_face_idx; //760;

		Mesh::FaceHandle start_face = mesh.face_handle(center_face_idx);
		Mesh::Point start(0,0,0);
		for (Mesh::FaceVertexIter fv_it  = mesh.fv_iter(start_face); fv_it; ++fv_it)
		{
		
			start [0] +=mesh.point(fv_it)[0];
			start [1] +=mesh.point(fv_it)[1];
			start [2] +=mesh.point(fv_it)[2];
		}
		start [0] /= 3.0f;
		start [1] /= 3.0f;
		start [2] /= 3.0f;

		std::list<Mesh::Point> path;
		std::vector<Mesh::FaceHandle> face_record;
		Backtracking<double>::draw_geodesics(mesh,  start, start_face,  end, vec_end_face_handle, path, face_record, 0.001);
		std::cout<<"Path Size "<<path.size()<<std::endl;
		for (std::list<Mesh::Point>::iterator it = path.begin(); it != path.end(); ++it)
		{
			std::cout<<(*it)<<" | ";
		}
		std::cout<<std::endl;
		std::cout<<"Face Size "<<face_record.size()<<std::endl;
		for (int i = 0; i < face_record.size(); ++i)
		{
			std::cout<<face_record[i].idx()<<' ';
		}
		std::cout<<std::endl;
		face_record.clear();
	
		std::cerr<<"Generate Mesh..."<<std::endl;
		//std::ofstream outfile("./geoArmadillo_20qem.obj");
		std::ofstream outfile(out_mesh);
		generate_seperate_mesh_geodesics(mesh, path, outfile);

	}
	void generate_iso_lines (const char * input_mesh, const char * out_mesh, unsigned start_vertex_idx, unsigned num_isolines)
	{
		//This test case is testing the interfere of isolines. 
		Mesh mesh;	
		bool result =  this->read_check_manifold_model(mesh, input_mesh);
		if (!result) {CPPUNIT_ASSERT_ASSERTION_FAIL("The model is invalid!"); return;}

		//this->face_centroid_fmm(mesh, 0);
		std::vector<int> point_idx_set;
		point_idx_set.push_back(start_vertex_idx);
		//point_idx_set.push_back(20);
		this->vertex_point_set_fmm (mesh, point_idx_set);
		unsigned n_vertices = mesh.n_vertices();

		Mesh::VertexIter v_it, v_end (mesh.vertices_end());
		std::vector<Mesh::Point> line;
		double max_dis = 0;
		for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
		{
//		std::cout<<mesh.data(v_it).get_distance()<<std::endl;;
			if (max_dis < mesh.data(v_it).get_distance())
			{
				max_dis = mesh.data(v_it).get_distance();
			}
		}
//	std::cout<<"MAX_DIS"<<max_dis<<std::endl;
		int n_lines = num_isolines;
		double step = max_dis / (n_lines + 1);
	
		double isovalue = 0;
		for (int i = 0; i < n_lines; ++i)
		{
			isovalue +=step;		
			draw_one_isoline(mesh, isovalue, line);
		}
	
		assert (line.size() % 2 == 0); 
		std::ofstream outfile(out_mesh);
		generate_seperate_mesh_isoline(mesh, line, outfile);
	}

};
#endif
